//
//  YCGenericable.swift
//  SwiftStudyDemo
//
//  Created by Fuhan on 2020/2/15.
//  Copyright © 2020 YCMath. All rights reserved.
//

import Foundation

public protocol Genericable {
    func equals(other: Any) -> Bool
    
    func setDetectPropValue<T>(instance: T, keyPath: PartialKeyPath<T>) -> Void
    func setDetectPropValue(instance: NSObject, keyPath: String) -> Void
    func setDetectPropValue(instance: StatesStoreable, keyPath: String, sectionName: String) -> Void
    
    static func detectPropValue() -> Any
}

extension Genericable {
    public func setDetectPropValue(instance: NSObject, keyPath: String) {
        instance.setValue(Self.detectPropValue(), forKey: keyPath)
    }
    
    public func setDetectPropValue(instance: StatesStoreable, keyPath: String, sectionName: String) -> Void {
        instance.setState(Self.detectPropValue(), byKey: keyPath, sectionName: sectionName)
    }
}

public func equalsType(lhs: Any, rhs: Any) -> (isEqual: Bool, isOptionalEqual: Bool, isLHSOptionalType: Bool) {
    let lhsType = Mirror(reflecting: lhs).subjectType
    let rhsType = Mirror(reflecting: rhs).subjectType
    if lhsType == rhsType {
        return (true, false, false)
    } else {
        let (lhsIsOptional, lhsRealTypeName) = parseOptionalType(lhsType)
        let (rhsIsOptional, rhsRealTypeName) = parseOptionalType(rhsType)
        let isEqualsType = lhsIsOptional != rhsIsOptional && lhsRealTypeName == rhsRealTypeName
        return (isEqualsType, true, lhsIsOptional)
    }
}

public func parseOptionalType(_ type: Any.Type) -> (Bool, String) {
    let optionalTypeStartStr = "Optional<"
    let typeName = String(describing: type)
    if typeName.hasPrefix(optionalTypeStartStr) {
        var realType = typeName.suffix(typeName.count - optionalTypeStartStr.count)
        realType = realType.prefix(realType.count - 1)
        return (true, String(realType))
    } else {
        return (false, typeName)
    }
}

protocol IntRawRepresentable {
    var intRawValue: Int { get }
}

extension IntRawRepresentable where Self: RawRepresentable, Self.RawValue == Int {
    var intRawValue: Int { return rawValue }
}

public func equalsOptionalAny(lhs: Any?, rhs: Any?) -> Bool {
    if let rhsValue = rhs {
        return equalsOptionalAny(lhs: lhs, rhsValue: rhsValue)
    } else {
        return equalsOptionalAny(lhs: lhs, rhsValue: rhs as Any)
    }
}

public func equalsOptionalAny(lhs: Any?, rhsValue: Any) -> Bool {
    let isNilAnyValue = "\(rhsValue)" == "nil"
    if let lhsValue = lhs {
        if isNilAnyValue {
            return false
        } else {
            return equalsAny(lhs: lhsValue, rhs: rhsValue)
        }
    } else if isNilAnyValue {
        return true
    }
    return false
}

public func equalsAny(lhs: Any, rhs: Any) -> Bool {
    let (isEqual, isOptionalEqual, isLHSOptionalType) = equalsType(lhs: lhs, rhs: rhs)
    if isEqual {
        if isOptionalEqual {
            if isLHSOptionalType {
                if let genericable = lhs as? Genericable {
                    return genericable.equals(other: rhs)
                }
            } else {
                if let genericable = rhs as? Genericable {
                    return genericable.equals(other: lhs)
                }
            }
        } else {
            if let genericable = lhs as? Genericable {
                return genericable.equals(other: rhs)
            } else {
                // try use castEnumToNativeValue() to detect
                let lhsValue = castEnumToNativeValue(value: lhs, rawType: "NSInteger")
                let rhsValue = castEnumToNativeValue(value: rhs, rawType: "NSInteger")
                if let lhsValue = lhsValue as? Int, let rhsValue = rhsValue as? Int {
                    return lhsValue == rhsValue
                }
            }
        }
    }
    return false
}

//////////////////////////////////////////

extension Bool : Genericable {
    public func equals(other: Any) -> Bool {
        if let other = other as? Bool {
            return self == other
        }
        return false
    }

    public func setDetectPropValue<T>(instance: T, keyPath: PartialKeyPath<T>) {
        if let keyPath = keyPath as? ReferenceWritableKeyPath<T, Bool> {
            instance[keyPath: keyPath] = !self
        }
    }
    
    public func setDetectPropValue(instance: NSObject, keyPath: String) {
        instance.setValue(!self, forKey: keyPath)
    }
    
    public func setDetectPropValue(instance: StatesStoreable, keyPath: String, sectionName: String) -> Void {
        instance.setState(!self, byKey: keyPath, sectionName: sectionName)
    }
    
    public static func detectPropValue() -> Any {
        return true
    }
}

extension Int : Genericable {
    public func equals(other: Any) -> Bool {
        if let other = other as? Int {
            return self == other
        }
        return false
    }

    public func setDetectPropValue<T>(instance: T, keyPath: PartialKeyPath<T>) {
        if let keyPath = keyPath as? ReferenceWritableKeyPath<T, Int> {
            instance[keyPath: keyPath] = 1989641989
        }
    }

    public static func detectPropValue() -> Any {
        return 1989641989
    }
}

extension UInt : Genericable {
    public func equals(other: Any) -> Bool {
        if let other = other as? UInt {
            return self == other
        }
        return false
    }

    public func setDetectPropValue<T>(instance: T, keyPath: PartialKeyPath<T>) {
        if let keyPath = keyPath as? ReferenceWritableKeyPath<T, UInt> {
            instance[keyPath: keyPath] = (1989641989 as UInt)
        }
    }

    public static func detectPropValue() -> Any {
        return 1989641989 as UInt
    }
}

extension Float : Genericable {
    public func equals(other: Any) -> Bool {
        if let other = other as? Float {
            return self == other
        }
        return false
    }

    public func setDetectPropValue<T>(instance: T, keyPath: PartialKeyPath<T>) {
        if let keyPath = keyPath as? ReferenceWritableKeyPath<T, Float> {
            instance[keyPath: keyPath] = 64.1989 as Float
        }
    }
    
    public static func detectPropValue() -> Any {
        // 198964.1989会被默认为Double类型
        return 64.1989 as Float
    }
}

extension Double : Genericable {
    public func equals(other: Any) -> Bool {
        if let other = other as? Double {
            return self == other
        }
        return false
    }

    public func setDetectPropValue<T>(instance: T, keyPath: PartialKeyPath<T>) {
        if let keyPath = keyPath as? ReferenceWritableKeyPath<T, Double> {
            instance[keyPath: keyPath] = 198964.1989
        }
    }
    
    public static func detectPropValue() -> Any {
        return 198964.1989 as Double
    }
}

extension CGRect : Genericable {
    public func equals(other: Any) -> Bool {
        if let other = other as? CGRect {
            return self == other
        }
        return false
    }

    public func setDetectPropValue<T>(instance: T, keyPath: PartialKeyPath<T>) {
        if let keyPath = keyPath as? ReferenceWritableKeyPath<T, CGRect> {
            instance[keyPath: keyPath] = CGRect(x: 198964.1989, y: 198964.1989, width: 198964.1989, height: 198964.1989)
        }
    }
    
    public static func detectPropValue() -> Any {
        return CGRect(x: 198964.1989, y: 198964.1989, width: 198964.1989, height: 198964.1989)
    }
}

extension CGSize : Genericable {
    public func equals(other: Any) -> Bool {
        if let other = other as? CGSize {
            return self == other
        }
        return false
    }

    public func setDetectPropValue<T>(instance: T, keyPath: PartialKeyPath<T>) {
        if let keyPath = keyPath as? ReferenceWritableKeyPath<T, CGSize> {
            instance[keyPath: keyPath] = CGSize(width: 198964.1989, height: 198964.1989)
        }
    }
    
    public static func detectPropValue() -> Any {
        return CGSize(width: 198964.1989, height: 198964.1989)
    }
}

extension CGPoint : Genericable {
    public func equals(other: Any) -> Bool {
        if let other = other as? CGPoint {
            return self == other
        }
        return false
    }

    public func setDetectPropValue<T>(instance: T, keyPath: PartialKeyPath<T>) {
        if let keyPath = keyPath as? ReferenceWritableKeyPath<T, CGPoint> {
            instance[keyPath: keyPath] = CGPoint(x: 198964.1989, y: 198964.1989)
        }
    }
    
    public static func detectPropValue() -> Any {
        return CGPoint(x: 198964.1989, y: 198964.1989)
    }
}

extension String : Genericable {
    public func equals(other: Any) -> Bool {
        if let other = other as? String {
            return self == other
        }
        return false
    }

    public func setDetectPropValue<T>(instance: T, keyPath: PartialKeyPath<T>) {
        if let keyPath = keyPath as? ReferenceWritableKeyPath<T, String> {
            instance[keyPath: keyPath] = "1989641989"
        }
    }
    
    public static func detectPropValue() -> Any {
        return "1989641989"
    }
}

extension Optional: Genericable {
    public func equals(other: Any) -> Bool {
        let (isEqual, isOptionalEqual, _) = equalsType(lhs: self as Any, rhs: other)
        if isEqual {
            if isOptionalEqual {
                if case .some(let wrapperValue) = self {
                    return equalsAny(lhs: wrapperValue, rhs: other)
                }
            } else {
                // Any类型可以强制转Wrapped类型
                let otherWrapped = other as? Wrapped
                
                // Optinal类型不可以强制转Wrapped类型，除了switch意外还可以用self.map { $0 }来解除wrapped
                switch self {
                case .some(let wrapped):
                    if let otherWrapped = otherWrapped {
                        return equalsAny(lhs: wrapped, rhs: otherWrapped)
                    } else {
                        return false
                    }
                default:
                    return otherWrapped == nil
                }
            }
        }
        return false
    }

    public func setDetectPropValue<T>(instance: T, keyPath: PartialKeyPath<T>) {
        if let keyPath = keyPath as? ReferenceWritableKeyPath<T, Optional<Wrapped>> {
            let currValue = instance[keyPath: keyPath]
            if let _ = currValue {
                instance[keyPath: keyPath] = nil
            } else {
                // 将Wrapped类型强转为Genericable的类型(静态)，然后获得：用于设置「探测属性的KeyPath」时的特殊值
                if let type = Wrapped.self as? Genericable.Type {
                    if let propValue = type.detectPropValue() as? Wrapped {
                        instance[keyPath: keyPath] = propValue
                    }
                }
            }
        }
    }
    
    public static func detectPropValue() -> Any {
        if let type = Wrapped.self as? Genericable.Type {
            if let propValue = type.detectPropValue() as? Wrapped {
                return Optional<Wrapped>.some(propValue) as Any
            }
        }
        return Optional<Wrapped>.none as Any
    }
}

extension Array : Genericable {
    public func equals(other: Any) -> Bool {
        if let other = other as? Array<Element>, self.count == other.count {
            for (idx, lhs) in self.enumerated() {
                let rhs = self[idx]
                if !equalsAny(lhs: lhs, rhs: rhs) {
                    return false
                }
            }
            return true
        }
        return false
    }

    public func setDetectPropValue<T>(instance: T, keyPath: PartialKeyPath<T>) {
        if let keyPath = keyPath as? ReferenceWritableKeyPath<T, Array<Element>> {
            if let type = Element.self as? Genericable.Type {
                if let propValue = type.detectPropValue() as? Element {
                    instance[keyPath: keyPath] = [propValue]
                }
            }
        }
    }
    
    public static func detectPropValue() -> Any {
        if let type = Element.self as? Genericable.Type {
            if let propValue = type.detectPropValue() as? Element {
                return [propValue]
            }
        }
        
        return [Element]()
    }
}

extension Dictionary : Genericable {
    public func equals(other: Any) -> Bool {
        if let other = other as? Dictionary<Key, Value>, self.count == other.count {
            for (key, value) in self {
                if let rhs = other[key], !equalsAny(lhs: value, rhs: rhs) {
                    return false
                }
            }
            return true
        }
        return false
    }

    public func setDetectPropValue<T>(instance: T, keyPath: PartialKeyPath<T>) {
        if let keyPath = keyPath as? ReferenceWritableKeyPath<T, Dictionary<Key, Value>>,
            let keyType = Key.self as? Genericable.Type, let valueType = Value.self as? Genericable.Type,
            let key = keyType.detectPropValue() as? Key, let value = valueType.detectPropValue() as? Value {
            instance[keyPath: keyPath] = [key : value]
        }
    }
    
    public static func detectPropValue() -> Any {
        if let keyType = Key.self as? Genericable.Type, let valueType = Value.self as? Genericable.Type {
            if let key = keyType.detectPropValue() as? Key, let value = valueType.detectPropValue() as? Value {
                return [key : value]
            }
        }
        fatalError("keyType: \(Key.self) or valueType: \(Value.self) not implement protocol `Genericable`")
    }
}
